/**
 * @protected 数学公式（katex.js）
 */

import { loadSource } from '../../utils/loader'
import { attribute, character, childList } from '../../utils/observer'
import { eachArray } from '../../utils/utils'
import BaseElement from '../core/base'

export default class KatexElement extends BaseElement {
    constructor() {
        super()

        /**
         * attribute 映射 css 属性名
         */
        this.map = new Map([
            ['align', 'text-align'],
            ['vertical', 'vertical-align'],
            ['size', 'font-size'],
            ['color', 'color'],
        ])

        const shadow = this.attachShadow({ mode: 'open' })
        shadow.innerHTML = `
            <style>
                :host { display: inline-block; }
                .root { display: inline-block; vertical-align: middle; }
            </style>
            <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.13.0/dist/katex.min.css">
            <div class="root"></div>`

        this.root = shadow.querySelector('.root')
    }

    render() {
        return loadSource('script', 'src', 'https://cdn.jsdelivr.net/npm/katex@0.13.0/dist/katex.min.js', { defer: 'defer', crossorigin: 'anonymous' }).then(() => {
            katex.render(this.textContent, this.root, {
                throwOnError: false,
            })
        })
    }

    connectedCallback() {
        // 将 style 同步到子跟节点
        if (this.attr.style) {
            this.root.setAttribute('style', this.attr.style)
        }

        this.render()

        // 将其它映射的 css attribute 同步到子跟节点
        Reflect.ownKeys(this.attr).forEach(k => {
            if (this.map.has(k)) {
                this.root.style.setProperty(this.map.get(k), this.attr[k])
            }
        })

        // attribute 变化监听
        const fn = records => {
            eachArray(records, ({ attributeName, oldValue }) => {
                switch (attributeName) {
                    case 'style':
                        const old = oldValue ? oldValue.split(';').map(css => css.split(':').shift().trim()) : []
                        const now = [...this.style]
                        old.forEach(k => {
                            if (!this.map.has(k)) {
                                this.root.style.removeProperty(k)
                            }
                        })
                        now.forEach(k => {
                            if (!this.map.has(k)) {
                                this.root.style.setProperty(k, this.style.getPropertyValue(k))
                            }
                        })
                        break
                    default:
                        if (this.map.has(attributeName)) {
                            console.log(attributeName)
                            const name = this.map.get(attributeName)
                            if (this.hasAttribute(attributeName)) {
                                this.root.style.setProperty(name, this.getAttribute(attributeName))
                            } else {
                                this.root.style.removeProperty(name)
                            }
                        }
                        break
                }
            })
        }

        /**
         * 监听 attribute 变化
         */
        attribute(this, fn, false, ['align', 'vertical', 'size', 'color', 'style'])

        /**
         * 监听子节点变化（通过 Element.textContent/Element.innerText 的方式修改公式表达式），负责重新渲染公式
         */
        childList(this, () => {
            this.render()
        })

        /**
         * 监听文本变化（通过 TextNode.textContent 的方式修改公式表达式），负责重新渲染公式
         */
        character(this, () => {
            this.render()
        })
    }
}